<template>
	<div class="container">
		<div class="left" ref="leftPanel">
			<div class="subtitle">模型版本：</div>
			<el-select placeholder="请选择模型版本" @change="switchModel" v-model="currentModel">
				<el-option v-for="item in modelList" :label="item" :value="item" ></el-option>
			</el-select>
			<div class="subtitle">置信度过滤：
				<span style="color: #409eff;">{{forecast.confidence}}</span>
			</div>
			<el-slider v-model="forecast.confidence" :max="1" :min="0.1" :step="0.01"/>
			<el-button type="primary" @click="imagesShow = true">训练过程展示</el-button>
		</div>
		<div class="right">
			<el-row justify="center">
				<el-col :span="24" style="text-align: center;">
					<span class="title">欢迎使用[{{ globalSubTitle }}]检测系统</span>
				</el-col>
			</el-row>
			<div style="display: flex; align-items: center; height: 350px; width: 100%; margin-bottom: 14px;">
				<div style="flex: 1; height: 100%; margin: 0 5px;">
					<el-card style="height: 100%; width: 100%;">
						<el-upload
							ref="upload"
							class="upload-demo"
							action="/api/yolo_model/uploadImage/"
							:on-success="handleUploadSuccess"
							:data="forecast"
							drag
							multiple
							:limit="1"
							:on-exceed="handleExceed"
						>
							<el-icon class="el-icon--upload">
								<upload-filled/>
							</el-icon>
							<div class="el-upload__text">
								拖拽文件到这 / <em>点击上传</em>
							</div>
							<template #tip>
								<div class="el-upload__tip">
									仅支持上传jpg/png文件，且不超过5MB
								</div>
							</template>
						</el-upload>
					</el-card>
				</div>
				<div style="flex: 1; height: 100%; margin: 0 5px;">
					<el-card style="height: 100%;">
						<div class="img_pre_wapper" v-if="currentImgResult.result_image && currentImgResult.bboxes.length !== 0">
							<div class="img_wapper">
								<el-image :src="`/api/${currentImgResult.result_image}`"
										  :preview-src-list="[`/api/${currentImgResult.result_image}`]"/>
							</div>
							<div class="right_wapper">
								<el-descriptions

									style="width: 100%;"
									title="识别结果"
									:column="1"
									border
								>
									<template #extra>
										<el-button type="primary"
												   @click="drawer = true">查看更多数据
										</el-button>
									</template>
									<el-descriptions-item>
										<template #label>
											<div class="cell-item">识别结果</div>
										</template>
										{{ currentImgResult.bboxes[0].category }}
									</el-descriptions-item>
									<el-descriptions-item>
										<template #label>
											<div class="cell-item">置信度</div>
										</template>
										{{ currentImgResult.bboxes[0].confidence }}
									</el-descriptions-item>
									<el-descriptions-item>
										<template #label>
											<div class="cell-item">宽度</div>
										</template>
										{{ currentImgResult.bboxes[0].bbox_w }}
									</el-descriptions-item>
									<el-descriptions-item>
										<template #label>
											<div class="cell-item">高度</div>
										</template>
										{{ currentImgResult.bboxes[0].bbox_h }}
									</el-descriptions-item>
									<el-descriptions-item>
										<template #label>
											<div class="cell-item">x坐标</div>
										</template>
										{{ currentImgResult.bboxes[0].bbox_x }}
									</el-descriptions-item>
									<el-descriptions-item>
										<template #label>
											<div class="cell-item">y坐标</div>
										</template>
										{{ currentImgResult.bboxes[0].bbox_y }}
									</el-descriptions-item>
								</el-descriptions>
							</div>
						</div>
						<el-empty description="暂无识别结果!"/>
					</el-card>
				</div>
			</div>


			<el-row style="height: 300px">
				<el-col :span="24">
					<hz-echarts-line-stack id="line2" ref="line2"/>
				</el-col>
			</el-row>

			<el-row style="height: 300px">
				<el-col :span="12">
					<hz-echarts-line-base id="line1" ref="line1"/>
				</el-col>
				<el-col :span="12">
					<hz-echarts-pie-base id="pie1" ref="pie1"/>
				</el-col>
			</el-row>


			<el-row>
				<el-col :span="24">
					<el-collapse v-model="activeNames" @change="handleChange" accordion>
						<el-collapse-item
							v-for="item in recognitionRecord"
							:key="item.id"
							:name="item.id"
						>
							<template #title>
								<div class="collapse-header">
									<el-tag type="info">ID: {{ item.id }}</el-tag>
									<span class="record-time">{{formatTime(item.time)}}</span>
									<el-badge :value="item.bboxes.length" type="primary" class="badge"/>
								</div>
							</template>
							<template #icon="{ isActive }">
              					<span class="icon-ele">{{ isActive ? '收起详情' : '展开详情' }}</span>
							</template>
							<el-row justify="center">
								<el-descriptions :column="2" direction="vertical" border>
									<el-descriptions-item label="上传图片" :span="1">
										<el-image
											:src="'api/'+item.upload_image"
											class="record-image"
											fit="cover"
											:preview-src-list="['api/'+item.upload_image]"
											:zoom-rate="1.2"
											:max-scale="7"
											:min-scale="0.2"
											hide-on-click-modal
											preview-teleported
										>
											<template #error>
												<div class="image-error">
													<el-icon>
														<Picture/>
													</el-icon>
													<span>加载失败</span>
												</div>
											</template>
										</el-image>
									</el-descriptions-item>
									<el-descriptions-item label="识别图片" :span="1">
										<el-image
											:src="'api/'+item.result_image"
											:preview-src-list="['api/'+item.result_image]"
											class="record-image"
											fit="cover"
											preview-teleported
										></el-image>
									</el-descriptions-item>
								</el-descriptions>
							</el-row>

							<el-row justify="center">
								<el-col :span="24">
									<el-table
										:data="item.bboxes"
										style="width: 100%"
										stripe
										border
										size="small"
										:row-class-name="tableRowClassName"
									>
										<el-table-column label="ID" prop="id" width="120" align="center"/>
										<el-table-column label="名称" prop="category" width="150" align="center"/>
										<el-table-column label="置信度" prop="confidence" width="120" align="center">
											<template #default="{row}">
												<el-tag :type="row.confidence > 0.8 ? 'success' : 'warning'">
													{{ (row.confidence * 100).toFixed(1) }}%
												</el-tag>
											</template>
										</el-table-column>
										<el-table-column label="x" prop="bbox_x" width="120" align="center"/>
										<el-table-column label="y" prop="bbox_y" width="120" align="center"/>
										<el-table-column label="w" prop="bbox_w" width="120" align="center"/>
										<el-table-column label="h" prop="bbox_h" width="120" align="center"/>
									</el-table>
								</el-col>
							</el-row>
						</el-collapse-item>
					</el-collapse>
				</el-col>
			</el-row>
		</div>


		<el-drawer
			v-model="drawer"
			title="当前识别结果"
			direction="rtl"
			size="50%"
		>
			<el-table :data="currentImgResult.bboxes" border stripe>
				<el-table-column type="index" align="center"/>
				<el-table-column prop="category" label="识别类型" align="center"/>
				<el-table-column prop="confidence" label="置信度" align="center">
					<template #default="{row}">

						<el-tag type="success" v-if="row.confidence >= 0.8">{{
								(row.confidence * 100).toFixed(1)
							}}%
						</el-tag>
						<el-tag type="warning" v-if="row.confidence < 0.8 && row.confidence > 0.5">
							{{ (row.confidence * 100).toFixed(1) }}%
						</el-tag>
						<el-tag type="danger" v-if="row.confidence < 0.5">{{
								(row.confidence * 100).toFixed(1)
							}}%
						</el-tag>
					</template>
				</el-table-column>
				<el-table-column prop="bbox_h" label="高度" align="center">
					<template #default="{row}">
						{{ (row.bbox_h).toFixed(2) }}
					</template>
				</el-table-column>
				<el-table-column prop="bbox_w" label="宽度" align="center">
					<template #default="{row}">
						{{ (row.bbox_w).toFixed(2) }}
					</template>
				</el-table-column>
				<el-table-column prop="bbox_y" label="y坐标" align="center">
					<template #default="{row}">
						{{ (row.bbox_y).toFixed(2) }}
					</template>
				</el-table-column>
				<el-table-column prop="bbox_x" label="x坐标" align="center">
					<template #default="{row}">
						{{ (row.bbox_x).toFixed(2) }}
					</template>
				</el-table-column>
			</el-table>
			<template #footer>
				<el-button type="primary" @click="drawer = false">关闭</el-button>
			</template>
		</el-drawer>

		<el-dialog v-model="imagesShow" title="训练过程">
			<div class="imgs-wapper" style="width: 100%; display: flex; flex-wrap: wrap; justify-content: space-evenly">
				<el-image v-for="(item, index) in traininfoImgs" :initial-index="index" :src="item" style="width: 200px; height: 200px;" preview-teleported :preview-src-list="traininfoImgs"/>
			</div>
			<template #footer>
				<el-button type="primary" @click="imagesShow = false">关闭</el-button>
			</template>
		</el-dialog>
	</div>
</template>

<script setup lang="ts">
import {ref, nextTick, onMounted} from "vue";
import {UploadFilled, Picture} from '@element-plus/icons-vue'
import {getYoloRecognitionRecord, getYoloStatistics, modelVersions, changeModel} from "/@api/yolo_model/hz-system-yolo-model-api";
import {CollapseModelValue, ElMessage, genFileId, UploadInstance, UploadProps, UploadRawFile} from "element-plus";
import type HzEchartsPieBase from "/@components/echarts/pie/Hz-Echarts-Pie-Base.vue";
import type HzEchartsLineBase from "/@components/echarts/line/Hz-Echarts-Line-Base.vue";
import type HzEchartsLineStack from "/@components/echarts/line/Hz-Echarts-Line-Stack.vue";
import {useMessage} from "/@hooks/message";
import dayjs from 'dayjs'

const globalSubTitle = ref(import.meta.env.VITE_GLOBAL_SUBTITLE?.trim() || 'HZSYSTEM');

const forecast = reactive({
	confidence: 0.5
})

//训练过程图片集弹窗
const imagesShow = ref(false)
const traininfoImgs = ref([
	'/api/static/yolo/traininfo/confusion_matrix.png',
	'/api/static/yolo/traininfo/confusion_matrix_normalized.png',
	'/api/static/yolo/traininfo/F1_curve.png',
	'/api/static/yolo/traininfo/labels.jpg',
	'/api/static/yolo/traininfo/labels_correlogram.jpg',
	'/api/static/yolo/traininfo/P_curve.png',
	'/api/static/yolo/traininfo/PR_curve.png',
	'/api/static/yolo/traininfo/R_curve.png',
	'/api/static/yolo/traininfo/results.png',
])


//右侧识别结果抽屉打开
const drawer = ref(false)


interface YoloRecord {
	id: string;
	time: string;
	bboxes: any[];
	upload_image: string;
	result_image: string;
}

const upload = ref<UploadInstance>()
const recognitionRecord = ref<YoloRecord[]>([]);

const pageConfig = ref({
	page: 1,
	pageSize: 10,
	total: 0,
});

const pie1 = ref<HzEchartsPieBase | null>(null);
const line1 = ref<HzEchartsLineBase | null>(null);
const line2 = ref<HzEchartsLineStack | null>(null);

onMounted(() => {
	loadYoLoRecognitionRecord();
	loadYoLoRecognitionStatistics();
	getModelVersions()
});

//1.YOLO识别记录
const loadYoLoRecognitionRecord = async () => {
	const response = await getYoloRecognitionRecord({
		page: pageConfig.value.page,
		pageSize: pageConfig.value.pageSize,
	});
	recognitionRecord.value = response.data.record;
};

//识别统计
const loadYoLoRecognitionStatistics = async () => {
	const response = await getYoloStatistics({});
	pie1.value?.refreshChart({
		title: '识别类型',
		subtext: '识别类型统计',
		yData: parseToPie(response.data?.statistics || {})
	});
	const lineData = parseToLine(response.data?.date_statistics || {});
	line1.value?.refreshChart({
		title: '识别使用趋势/分',
		xData: lineData.xData,
		yData: lineData.yData
	});
	const lineStackData = parseToLineStack(response.data?.date_category_statistics || {});
	line2.value?.refreshChart({
		title: '识别使用趋势/秒',
		xData: lineStackData.xData,
		yData: lineStackData.yData,
		legendData: lineStackData.yData.map(item => item.name),
	});
};


const activeNames = ref(['1'])
const handleChange = (val: CollapseModelValue) => {
	console.log(val)
}

//当前上传图片的返回结果
const currentImgResult = ref({})
//图片(自动)上传成功调用
const handleUploadSuccess = (res: any, file: any, fileList: any) => {
	if(res.code !== 200){
		useMessage().error(res.msg)
		return
	}
	currentImgResult.value = res.data
	if (currentImgResult.value.bboxes.length >= 1) {
		useMessage().success('图片识别成功')
		drawer.value = true
	}
	if(currentImgResult.value.bboxes.length == 0){
		useMessage().warning('未识别到任何物体')
	}

	loadYoLoRecognitionRecord();
	loadYoLoRecognitionStatistics();
}

const tableRowClassName = (row: any) => {
	if (row.confidence > 0.8) {
		return 'success-row';
	} else if (row.confidence > 0.5) {
		return 'warning-row';
	} else {
		return '';
	}
}

const handleExceed: UploadProps['onExceed'] = (files) => {
	upload.value!.clearFiles()
	const file = files[0] as UploadRawFile
	file.uid = genFileId()
	upload.value!.handleStart(file)
	// 执行上传
	upload.value!.submit()
}


const parseToPie = (data: any) => {
	const res = Object.keys(data).map(key => {
		return {
			name: key,
			value: data[key]
		}
	});
	return res
}

const parseToLine = (data: any) => {
	return {
		xData: Object.keys(data),
		yData: Object.values(data)
	}
}

const parseToLineStack = (data: any) => {
	const xData = Object.keys(data);
	const yData = [];
	for (const key in data) {
		const item = data[key];
		for (const category in item) {
			const index = yData.findIndex((value) => value.name === category);
			if (index === -1) {
				yData.push({
					name: category,
					type: 'line',
					stack: 'Total',
					data: [item[category]]
				});
			} else {
				yData[index].data.push(item[category]);
			}
		}
	}
	return {
		xData: xData,
		yData: yData
	}

}



//当前选择的模型
const currentModel = ref('')
//切换模型
const switchModel = async (model) => {
	const res = await changeModel(model)
	ElMessage({
		type: res.code == 200 ? 'success' : 'error',
		message: res.msg,
		duration: 2000
	})
}
//获取模型下拉
const modelList = ref([])
const getModelVersions = async () => {
	const res = await modelVersions()
	modelList.value = res.data.model_version
	currentModel.value = modelList.value[0]
}

//格式化时间
const formatTime = time => {
	return dayjs(time).format('YYYY-MM-DD HH:mm:ss')
}
</script>

<style scoped lang="css">
/* 禁止文字选中 */
.no-select {
	user-select: none;
}

.container {
	width: 100%;
	height: calc(100vh - 60px);
	display: flex;

	.left, .right {
		height: 100%;
		overflow-y: scroll;
		/* 隐藏 WebKit 浏览器的滚动条 */

		&::-webkit-scrollbar {
			display: none;
		}

		/* 隐藏 Firefox 浏览器的滚动条 */
		scrollbar-width: none;
	}

	.left {
		width: 220px;
		height: 100%;
		overflow-y: scroll;
		border-right: 1px solid rgb(177.3, 179.4, 183.6);
		display: flex;
		flex-direction: column;
		position: relative;
		padding: 10px;

		.subtitle {
			width: 100%;
			height: 30px;
			line-height: 30px;
			text-align: left;
			color: #303133;
		}

	}

	.right {
		flex: 1;
		padding: 20px 20px;
	}
}

.img_pre_wapper {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;

	.img_wapper, .right_wapper {
		height: 100%;
	}

	.img_wapper {
		width: 40%;
	}

	.right_wapper {
		width: 60%;
		padding-left: 8px;
		display: flex;
		flex-direction: column;
		align-items: flex-start;
	}
}


.title {
	font-size: 28px;
	color: #303133;
	letter-spacing: 1.2px;
	margin: 20px 0;
	display: inline-block;
	background: linear-gradient(45deg, #409eff, #79bbff);
	-webkit-background-clip: text;
	-webkit-text-fill-color: transparent;
}

.upload-demo {
	margin: 30px 0;
}

.el-row {
	margin-bottom: 20px;
}

.icon-ele {
	margin: 0 8px 0 auto;
	color: #409eff;
}

.recordTitle {
	font-size: 18px;
	font-weight: bold;
}

.recordImage {
	width: 200px;
	height: 200px;
}

.chart-card {
	margin: 10px;
	border-radius: 8px;
	transition: all 0.3s;
}

.chart-card:hover {
	box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
}

.record-card {
	margin: 10px 0;
	border-radius: 8px;
	background: #f8f9fa;
}

.collapse-header {
	display: flex;
	align-items: center;
	gap: 15px;
	padding: 12px 0;
}

.record-time {
	color: #606266;
	font-size: 14px;
}

.badge {
	margin-left: auto;
}

.record-image {
	width: 200px;
	height: 200px;
	border-radius: 6px;
	transition: transform 0.3s;
	cursor: pointer;
}

.record-image:hover {
	transform: scale(1.05);
}

.image-error {
	@apply h-full w-full flex flex-col items-center justify-center bg-gray-100 text-gray-400;
}

:deep(.el-table) {
	margin: 15px 0;
	--el-table-border-color: #ebeef5;
	--el-table-header-bg-color: #f8f9fa;
}

:deep(.el-table__row) {
	transition: all 0.3s;
}

:deep(.el-table__row:hover) {
	transform: translateY(-2px);
	box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
</style>
